package Regex
{
    import flash.events.Event;
    
    import mx.collections.ArrayCollection;
    
    [Event(name=ProgressEvent.COMPLETE, type="flash.events.ProgressEvent")]

    [Bindable]
    public class Regex
    {
        private var _pattern:Object;
        private var _flags:String = "gi";
        private var _replacement:String = "";
        private var _description:String = "";
        private var _match:Object;
        private var _count:Number = 0;
        private var _re:RegExp;
        private var _timings:Array = new Array(5);

        public function Regex(  pattern:Object, 
                                flags:String = "", 
                                replacement:String = "", 
                                description:String = "")
//                                isUnAccent:Boolean = false)
        {
            _pattern = pattern;
            if( typeof pattern != 'object' ) {
                _flags = flags.indexOf("g") < 0 ? "g" + flags : flags ;
                _re = new RegExp(pattern, _flags);
            }
            else {
                _re = new RegExp( pattern );
            }
            _replacement = replacement;
            _description = description;
        }
        
        public function get RegularExpression():RegExp {
            return this._re;
        }
        
        public function set Count(n:Number):void {
            this._count = n;
        }
        
        public function get Count():Number {
            return this._count;
        }
        
        public function get CountString():String {
            return _count == 0 ? "" : this._count.toString();
        }
        public function get lastIndex():Number {
            return _re.lastIndex;
        }
        
        public function set lastIndex(n:Number):void {
            _re.lastIndex = n;
        }
        
        // either need to supply both s and offset with "g" on, or neither ...
        public function NextMatch(s:String, offset:Number):Object {
            // if s is provided, match from the beginning
            if( offset >= 0) {
                this._re.lastIndex = offset;
            }

             // exec result: [0] is the matched text, [1...] are parenthesized element matches.
            // Also set are _match.length, _match.index (1st char of matched text), 
            // lastindex is position just after the match
           if(!s) {
                this._match = this._re.exec();
            }
            else {
                this._match = this._re.exec(s);
            }
            return this._match;
        }  
        
        public function PrevMatch(s:String = null, offset:Number = -1):Object {
            var pos:Number;
            var o:Object;
            // establish position to search back from.
            // it's offset if offset is provided.
            if(offset >= 0) {
                pos = offset;
            }
            else if(s) {
                pos = s.length;
            }
            else if(this._match) {
                pos = _match.index;
            }
            
            this._re.lastIndex = 0;
            this._match = null;
            for( ; ; ) {
                if(s) {
                    o = this._re.exec(s);
                }
                else {
                    o = this._re.exec();
                }
                if(! o) {
                    break;
                }
                if(o.index >= pos) { // beyond it
                    break;
                }
                this._match = o;                    
            }
            return this._match;
        }
        
        public function MatchCount(s:String):Number {
            var a:Object = s.match(this._re);
            return a ? a.length : 0;
        }
        
        public function Matches(s:String):ArrayCollection {
            if(_pattern.length <= 0) {
                return null;
            }
            var ac:ArrayCollection = new ArrayCollection;
            for( ; ; ) {
                var o:Object = _re.exec(s);
                if(! o) {
                    break;
                }
                ac.addItem(o);
            }
            return ac;
        }
        
        // given a string returned from the quotes regex, return it properly reconstructed        
        public function FixedQuote(squote:String):String {
            var src:String = _re.source.toString();
            var f:String = this.Flags.replace("g","");
            var re:RegExp = new RegExp(src, f);
//            trace(re.lastIndex, re.test(squote));
            var o:Object = squote.match(re);
            
            if(! o ) {
                return squote;
            }

            var s:String;
            var qmatch:String = o[0];        
            var qpfx:String = o[1];
            var qtext:String = o[3];
            var qrtspc:String = o[5];
            var qrtquote:String = o[6];
            var qsfx:String = o[7];
            if(qrtspc == "\r" && qrtquote == '"') {
                qrtspc = "";
                qsfx = "\n" + qsfx;
            }
            else if(qsfx.length > 0) {
                qsfx = ' ' + qsfx;
            }
            if(qpfx.length > 0) {
                qpfx += ' ';
            }
            s = qpfx + '"' + qtext + '"' + qsfx;
            s = s.replace(/\r/g, "\n");

//            trace(s);
            return s;
        }
    
        
        public function get PatternString():String {
                return _pattern.toString();
        }
        
        public function set PatternString(s:String):void {
            _pattern = s;
        }
        
        public function get Description():String {
            return _description;
        }
        
        public function ReplaceString(s:String):String {
        	
            return s.replace(this._re, this._replacement);
        }
        
        public function get ReplacePattern():String {
            return _replacement;
        }
        
        public function set ReplacePattern(s:String):void {
            _replacement = s;
        }
        
        public function set Flags(s:String):void {
            _flags = s;
        }
        
        public function get Flags():String {
            return _flags;
        }
        
        public function get global():Boolean {
            return (_flags.indexOf("g") >= 0);
        }
        
        public function set global(b:Boolean):void {
            if(b && ! global) {
                _flags += "g";
            }
            else if(!b && global) {
                unsetGlobal();
            }
        }
        
        public function unsetGlobal():void {
            _flags.replace("g", "");            
        }
      
        public function get multiline():Boolean {
            return (_flags.indexOf("m") >= 0);
        }
        
        public function set multiline(b:Boolean):void {
            if(b && ! multiline) {
                _flags += "m";
            }
            else if(!b && multiline) {
                unsetMultiline();
            }
        }
      
        public function unsetMultiline():void {
            _flags.replace("m", "");            
        }
      
        public function get ignorecase(): Boolean {
            return (_flags.indexOf("i") >= 0);
        }
        
        public function set ignorecase(b:Boolean):void {
            if(b && ! ignorecase) {
                _flags += "i";
            }
            else if(! b && ignorecase) {
                unsetIgnoreCase();
            }
        }
      
        public function unsetIgnoreCase():void {
            _flags.replace("i", "");            
        }
      
        public function get unaccent(): Boolean {
            return (_flags.indexOf("u") >= 0);
        }
        
        public function set unaccent(b:Boolean):void {
            if(b && ! unaccent) {
                _flags += "u";
            }
            else if(!b && unaccent) {
                unsetUnaccent();
            }
        }
      
        public function unsetUnaccent():void {
            _flags.replace("u", "");            
        }
      
        public function get dotall():Boolean {
            return (_flags.indexOf("d") >= 0);
        }
        
        public function set dotall(b:Boolean):void {
            if(b && ! dotall) {
                _flags += "d";
            }
            else if(! b && dotall) {
                unsetDotall();
            }
        }
      
        public function unsetDotall():void {
            _flags.replace("u", "");            
        }
      
        public function set timing(val:Number):void {
            _timings.push(val);
        }
        
        public function echoTimings():void {
            trace("timings", _timings);
        }
    }

}
